
#include <math.h>
#include "../iface/testerror.h"
#include <apr_strings.h>
#include "../db/Actor.h"
#include "LocalResolver.h"

/** Constructor for the local resolver class.
 * 
 * @param iactorsdb The actors database to be queried for actor information.
 * 
 * @param imp The memory pool to use to derive a new set of memory pools for this class. */
LocalResolver::LocalResolver(ActorsDB *iactorsdb, apr_pool_t *imp) :
   Resolver(imp)
{
   FATAL_IF_NULL(, iactorsdb, "actor database pointer");
   actorsdb = iactorsdb;
   valid = 1;
}

/** Destructor for the local resolver class. */
LocalResolver::~LocalResolver()
{
}

/** Rename the human-readable part of an actor.
 * 
 * @param actorid The unique internal ID of the actor.
 * 
 * @param actoralias The alias of the actor. */
void LocalResolver::renameActorID(const char *actorid, const char *actoralias)
{
   if (validateNewNameNotIllegal(actoralias) < 0)
      errprint("illegal actor alias '%s'", actoralias);
   
}

/** Resolve a name (internal ID) from the given human-readable namespace hierarchy.
 * 
 * @param hierarchy The human-readable namespaced name.
 * 
 * @return The internal ID, or NULL if it was not found. */
const char *LocalResolver::resolveName(const char *hierarchy)
{
   if (!valid)
   {
      errprint("invalid local resolver - can't resolve");
      return NULL;
   }
   FATAL_IF_BLANK(NULL, hierarchy, "hierarchical query");
   int failed_parent_lookup = 0;
   const char *known_parent = NULL;
   const char *child_ext_name = NULL;
   if (strstr(hierarchy, ".") != NULL)
   {
      // this is a query spanning multiple hierarchy levels,
      // so we can resolve recursively.
      const char *parent_name = apr_pstrdup(mp, hierarchy);
      int last_dot_idx = 0;
      for (int i = 0; i < strlen(parent_name); i++)
         if (parent_name[i] == '.')
            last_dot_idx = i;
      // last_dot_idx must exist, so we call...
      (const_cast<char *>(parent_name))[last_dot_idx] = '\0';
      known_parent = resolveName(parent_name);
      if (known_parent == NULL)
      {
         failed_parent_lookup = 1;
         errprint("lookup of '%s' failed", parent_name);
      }
      // doubly adding things to the map causes problems!
      // by resolving the name, we've already added it!
      child_ext_name = apr_pstrdup(mp, &(hierarchy[last_dot_idx + 1]));
   } else
   {
      child_ext_name = apr_pstrdup(mp, hierarchy);
   }
   // now perform a lookup
   // ... however
   // if the actor exists and it's a real ID already without the external name lookup,
   // then we return that... because we can do security checks on the server side after
   // resolution, and that should be okay
   if (actorsdb->testActorExists(child_ext_name))
      return cacheAndReturn(hierarchy, child_ext_name);
   // now we try resolving the actor to something that already exists
   char *real_id = NULL;
   if (known_parent)
   {
      // if we know the parent, then we use the parent as a constraint...
      actorsdb->sqlGetSingleStringf(&real_id, "select name from 'actors' where parent='%s' and externalalias='%s';", known_parent, child_ext_name);
      const char *returnable = cacheAndReturn(hierarchy, real_id);
      std::string nstring(std::string(hierarchy) + std::string(".name"));
      returnable = cacheAndReturn(nstring.c_str(), child_ext_name);
      std::string nstring2(std::string(hierarchy) + std::string(".id"));
      returnable = cacheAndReturn(nstring2.c_str(), real_id);
      if (real_id) free(real_id);
      return returnable;
   } else
   {
      if (!failed_parent_lookup)
      {
         // if we don't know the parent, and this isn't part of a failed hierarchy, we look up only on the external alias;
         // a bit dangerous, but what can we do? (apart from abort?)
         errprint("warning: looking up '%s' without real uniqueness checking", child_ext_name);
         actorsdb->sqlGetSingleStringf(&real_id, "select name from 'actors' where externalalias='%s';", child_ext_name);
         const char *returnable = cacheAndReturn(hierarchy, real_id);
         std::string nstring(std::string(hierarchy) + std::string(".name"));
         returnable = cacheAndReturn(nstring.c_str(), child_ext_name);
         std::string nstring2(std::string(hierarchy) + std::string(".id"));
         returnable = cacheAndReturn(nstring2.c_str(), real_id);
         if (real_id) free(real_id);
         return returnable;
      }
   }
   return NULL;
}

/** Resolve the type of the externally named actor using the database.
 * We can cache all these results, actors can't change type.
 * 
 * @param hierarchy The hierarchical external namespacing string
 * that uniquely identifies this actor.
 * 
 * @return The type of the actor. This is one of 'users', 'actors',
 * 'computes', 'workinstances' or 'datastores'. */
const char *LocalResolver::resolveType(const char *hierarchy)
{
   if (!valid)
   {
      errprint("invalid local resolver - can't resolve");
      return NULL;
   }
   FATAL_IF_BLANK(NULL, hierarchy, "hierarchical query");
   const char *resname = resolveName(hierarchy);
   if (resname == NULL) return NULL;
   char *resolved_typename = NULL;
   actorsdb->sqlGetSingleStringf(&resolved_typename, "select class from 'actors' where name='%s';", resname);
   std::string nstring(std::string(hierarchy) + std::string(".type"));
   const char *returnable = cacheAndReturn(nstring.c_str(), resolved_typename);
   if (resolved_typename) free(resolved_typename);
   return returnable;
}

/** Resolves the progress indicator of the externally named actor
 * using the internal database. We can cache the result when this
 * is complete, but until then this needs to be updated in real-time.
 * 
 * @param hierarchy The hierarchical external namespacing string
 * that uniquely identifies this actor.
 * 
 * @return The completion amount as a string. -1 on error, or if the request
 * doesn't make sense, 0 for no progress, 1 for complete, all
 * decimals from 0 to 1 mark out varying stages of 'completion'
 * for this actor. */
const char *LocalResolver::resolveProgress(const char *hierarchy)
{
   if (!valid)
   {
      errprint("invalid local resolver - can't resolve");
      return NULL;
   }
   FATAL_IF_BLANK(NULL, hierarchy, "hierarchical query");
   const char *resname = resolveName(hierarchy);
   if (resname == NULL) return NULL;
   std::vector<char *> *stringlist;
   stringlist = actorsdb->sqlScanAllTextf("select blocksdone,blockstotal from 'actors' where name='%s';", resname);
   if (stringlist == NULL) { errprint("could not get progress"); return NULL; }
   if (stringlist->size() != 2) { errprint("progress data not in expected format"); return NULL; }
   double numerator_blocks, denominator_blocks;
   sscanf(stringlist->at(0), "%lf", &numerator_blocks);
   sscanf(stringlist->at(1), "%lf", &denominator_blocks);
   StringUtils::destroyStringList(stringlist);
   if (fabs(denominator_blocks) == 0.)
   {
      // cache the invalidity of this request
      const char *string_error = "-1.0";
      return cacheTempAndReturn(hierarchy, string_error);
   }
   double resultant_completion = (numerator_blocks / denominator_blocks);
   char *double_string_instantiate = StringUtils::varprintf("%lf", resultant_completion);
   const char *returnable = NULL;
   std::string nstring(std::string(hierarchy) + std::string(".progress"));
   if ((resultant_completion < 1.) && (double_string_instantiate))
   {
      returnable = cacheTempAndReturn(nstring.c_str(), double_string_instantiate);
   } else
   {
      returnable = cacheAndReturn(nstring.c_str(), double_string_instantiate);
   }
   if (double_string_instantiate) free(double_string_instantiate);
   return returnable;
}

/** Resolve metadata about a given hierarchy-named actor.
 * 
 * @param parent The human-readable namespaced name of the actor whose
 * metadata is to be queried.
 * 
 * @param suffix The suffix denoting which metadata piece is to be
 * returned from this function.
 * 
 * @return The data as a string. It is NULL if it is not found, or an
 * empty string if there is no data. */
const char *LocalResolver::resolveMetadata(const char *parent, const char *suffix)
{
   if (!valid)
   {
      errprint("invalid local resolver - can't resolve");
      return NULL;
   }
   FATAL_IF_BLANK(NULL, parent, "parent hierarchical query");
   FATAL_IF_BLANK(NULL, suffix, "suffix for information required");
   const char *resname = resolveName(parent);
   if (resname == NULL) return NULL;
   Actor tactor(actorsdb);
   tactor.setName(resname);
   APRTable *ttable = tactor.getMetadata(mp);
   int count = 0;
   while (Resolver::metadata_names[count].simple_key != NULL)
   {
      if (ttable->get("%s", metadata_names[count].broker_message_key))
      {
         // this one is in the metadata received
         Resolver::cacheabletype cache_it = metadata_names[count].cacheable;
         int should_cache = 0;
         const char *result = ttable->get("%s", metadata_names[count].broker_message_key);
         if ((strlen(result) > 0) && (cache_it == Resolver::ITEM_CACHEABLE_IF_NONEMPTY))
            should_cache = 1;
         if (cache_it == Resolver::ITEM_CACHEABLE_ALWAYS)
            should_cache = 1;
         if (should_cache)
         {
            caching_map[std::string(parent) + std::string(".") + std::string(metadata_names[count].simple_key)] = std::string(result);
         } else
         {
            // caching it anyway, but shouldn't rely on the accuracy of this without updates
            caching_map[std::string(parent) + std::string(".") + std::string(metadata_names[count].simple_key) + std::string("_cached")] = std::string(result);
         }
      }
      //else errprint("didn't get '%s' (%s) in metadata", metadata_names[count].broker_message_key, metadata_names[count].simple_key);
      count++;
   }
   const char *retval = NULL;
   count = 0;
   while (Resolver::metadata_names[count].simple_key != NULL)
   {
      if (strcmp(metadata_names[count].simple_key, suffix) == 0)
      {
         Resolver::cacheabletype cache_it = metadata_names[count].cacheable;
         int should_cache = 0;
         const char *result = ttable->get("%s", metadata_names[count].broker_message_key);
         if (result)
         {
            if ((strlen(result) > 0) && (cache_it == Resolver::ITEM_CACHEABLE_IF_NONEMPTY))
               should_cache = 1;
            if (cache_it == Resolver::ITEM_CACHEABLE_ALWAYS)
               should_cache = 1;
            std::string nstring(std::string(parent) + std::string(".") + std::string(metadata_names[count].simple_key));
            if (should_cache)
            {
               retval = getCached(nstring.c_str());
            } else
            {
               retval = getTempCached(nstring.c_str());
            }
         }
      }
      count++;
   }
   if (retval == NULL)
      errprint("metadata '%s' not found for parent hierarchy '%s'", suffix, parent);
   return retval;
}

/** Resolve data about the parent of a given hierarchy-named actor.
 * 
 * @param parent The human-readable namespaced name of the actor whose
 * parent is to be queried.
 * 
 * @param suffix The suffix denoting what data is to be gathered about
 * the parent.
 * 
 * @return The data as a string. It is NULL if it is not found, or an
 * empty string if there is not parent. */
const char *LocalResolver::resolveParent(const char *parent, const char *suffix)
{
   if (!valid)
   {
      errprint("invalid local resolver - can't resolve");
      return NULL;
   }
   FATAL_IF_BLANK(NULL, parent, "parent hierarchical query");
   FATAL_IF_BLANK(NULL, suffix, "suffix for information required");
   const char *resname = resolveName(parent);
   if (resname == NULL) return NULL;
   Actor tactor(actorsdb);
   tactor.setName(resname);
   const char *parent_name = tactor.getParent();
   Actor tpactor(actorsdb);
   tpactor.setName(parent_name);
   const char *parent_ext_name = tactor.getExternalAlias();
   std::string nstring(std::string(parent) + std::string(".parentname"));
   cacheAndReturn(nstring.c_str(), parent_ext_name);
   std::string nstring2(std::string(parent) + std::string(".parent"));
   cacheAndReturn(nstring2.c_str(), parent_name);
   if (strcmp(suffix, "parentname") == 0) return getCached(nstring.c_str());
   if (strcmp(suffix, "parent") == 0) return getCached(nstring2.c_str());
   return NULL;
}

/** Resolve data about the children of a given hierarchy-named actor.
 * 
 * @param parent The human-readable namespaced name of the actor whose
 * children are to be queried.
 * 
 * @param suffix The suffix denoting what data is to be gathered about
 * the children.
 * 
 * @return The data as a string. It is NULL if it is not found, or an
 * empty string if there are no children. */
const char *LocalResolver::resolveChildren(const char *parent, const char *suffix)
{
   if (!valid)
   {
      errprint("invalid local resolver - can't resolve");
      return NULL;
   }
   FATAL_IF_BLANK(NULL, parent, "parent hierarchical query");
   FATAL_IF_BLANK(NULL, suffix, "suffix for information required");
   const char *resname = resolveName(parent);
   if (resname == NULL) return NULL;
   std::vector<char *> child_ids;
   std::vector<char *> child_names;
   actorsdb->getChildren(&child_ids, resname);
   actorsdb->getExternalAliasesOfChildren(&child_names, resname);
   char *ids_list = StringUtils::flattenStringList(&child_ids, ",");
   char *names_list = StringUtils::flattenStringList(&child_names, ",");
   std::string nstring(std::string(parent) + std::string(".childrennames"));
   cacheAndReturn(nstring.c_str(), names_list);
   std::string nstring2(std::string(parent) + std::string(".children"));
   cacheAndReturn(nstring2.c_str(), ids_list);
   if (strcmp(suffix, "childrennames") == 0) return getCached(nstring.c_str());
   if (strcmp(suffix, "children") == 0) return getCached(nstring2.c_str());
   return NULL;
}
